package main

import (
	"bufio"
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"net"
	"os"
	"time"
)

/**
  基于libpcap+gopacket 抓包解包发包
  单个IP阻断运行命令 ./libpcapAccess.go [要阻断的IP] [BPF]
*/
var eth layers.Ethernet
var dot1q layers.Dot1Q
var ipv4 layers.IPv4
var tcp layers.TCP
var handleMirror *pcap.Handle
var handleSend *pcap.Handle
var err error
var ip net.IP
var parser gopacket.DecodingLayerFunc
var decoded []gopacket.LayerType

func main() {
	fmt.Println("pcap version: ", pcap.Version())
	// 获取命令行参数
	//1.0 获取第二个参数,设置阻断IP,单个
	ip = net.ParseIP(os.Args[1])
	if ip == nil {
		panic("please enter the correct ip not " + ip.String())
	}
	//2.0 获取第三个参数,设置BPF
	bpf := os.Args[2]
	//3.0 读取控制台输入的网卡
	devs, _ := pcap.FindAllDevs()
	fmt.Println("Please enter the network card, for example eth0")
	for i := range devs {
		fmt.Println(devs[i].Name)
	}
	fmt.Println("Please enter receive:")
	in := bufio.NewReader(os.Stdin)
	str := make([]byte, 1024)
	str, _, err = in.ReadLine()
	if err != nil {
		panic(err)
	}
	deviceId := string(str)
	ip = net.ParseIP(os.Args[1])
	if ip == nil {
		panic("please enter the correct ip not " + ip.String())
	}
	handleMirror, err = pcap.OpenLive(
		deviceId, // network device
		int32(65535),
		true,
		time.Microsecond,
	)
	if err != nil {
		fmt.Println("Mirror Handler error", err.Error())
		return
	}
	fmt.Println("Please enter send:")
	in = bufio.NewReader(os.Stdin)
	str = make([]byte, 1024)
	str, _, err = in.ReadLine()
	if err != nil {
		panic(err)
	}
	deviceId = string(str)
	handleSend, err = pcap.OpenLive(
		deviceId, // network device
		int32(65535),
		false,
		time.Microsecond,
	)
	if err != nil {
		fmt.Println("Mirror Handler error", err.Error())
		return
	}

	err := handleMirror.SetBPFFilter(bpf)
	if err != nil {
		return
	}
	fmt.Println("The program has been started")
	fmt.Println("当前阻断IP:", ip)
	fmt.Println("当前BPF:", bpf)
	fmt.Println("当前deviceId:", deviceId)

	dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
	dlc = dlc.Put(&eth)
	dlc = dlc.Put(&dot1q)
	dlc = dlc.Put(&ipv4)
	dlc = dlc.Put(&tcp)
	parser = dlc.LayersDecoder(layers.LayerTypeEthernet, gopacket.NilDecodeFeedback)
	decoded = make([]gopacket.LayerType, 0, 20)
	for true {
		data, _, _ := handleMirror.ReadPacketData()
		analysePacket(data)
	}
}

func analysePacket(packet []byte) {
	lt, err := parser(packet, &decoded)
	if err != nil {
		//fmt.Fprintf(os.Stderr, "无法解码层：%v\n", err)
		return
	}
	if lt != gopacket.LayerTypeZero {
		//fmt.Fprintf(os.Stderr, "未知层类型: %v\n", lt)
		return
	}
	fmt.Println("1srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	fmt.Println("1srcMac: ", eth.SrcMAC.String(), "dstMac: ", eth.DstMAC.String())
	if !ip.Equal(ipv4.SrcIP) {
		return
	}
	fmt.Println("2srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	//if tcp.SYN && !tcp.ACK {
	//	err = SendFlag(eth.DstMAC, eth.SrcMAC, ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, 10000, tcp.Seq+1, true, true, false)
	//	fmt.Println("send syn & ack")
	//}
	//5.0 过滤掉SYN FIN RST 和 http
	if tcp.SYN || tcp.FIN || tcp.RST {
		//log.Println("send nothing")
		return
	}

	//握手完成开始推送数据时候拦截
	win := tcp.Window
	loop := 5
	count := loop
	for count > 0 {
		//阻断主要是发送的SEQ序列号和上一位的ACK值相等
		seq := tcp.Ack + uint32(uint16(loop-count)*win)
		err = SendFlag(eth.DstMAC, eth.SrcMAC, ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, seq, 10000, false, false, true)
		if err != nil {
			fmt.Println(err)
		}
		count = count - 1
	}
	fmt.Println("send rst")
}

func SendFlag(srcMac, dstMac net.HardwareAddr, srcIp, dstIp net.IP, srcPort, dstPort layers.TCPPort, seq, ack uint32, SYN, ACK, RST bool) error {
	s := time.Now().UnixNano()
	eth.SrcMAC = srcMac
	eth.DstMAC = dstMac
	ipv4.SrcIP = srcIp
	ipv4.DstIP = dstIp
	tcp.SrcPort = srcPort
	tcp.DstPort = dstPort
	tcp.SYN = SYN
	tcp.Seq = seq
	tcp.ACK = ACK
	tcp.Ack = ack
	tcp.RST = RST

	options := gopacket.SerializeOptions{
		FixLengths:       true,
		ComputeChecksums: true,
	}

	if err = tcp.SetNetworkLayerForChecksum(&ipv4); err != nil {
		panic(err)
	}
	buffer := gopacket.NewSerializeBuffer()

	err = gopacket.SerializeLayers(buffer, options, &eth, &ipv4, &tcp)
	if err != nil {
		panic(err)
	}
	err = handleSend.WritePacketData(buffer.Bytes())
	if err != nil {
		return err
	}
	fmt.Println("3srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	fmt.Println("3srcMac: ", eth.SrcMAC.String(), "dstMac: ", eth.DstMAC.String())
	fmt.Println("send time", time.Now().UnixNano()-s)
	return nil
}
